package com.sunsy.authserver.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sunsy.authserver.beans.dto.AccessTokenDTO;
import com.sunsy.authserver.beans.po.AppClient;
import com.sunsy.authserver.beans.vo.AccessTokenVO;
import com.sunsy.authserver.dao.AccessTokenDao;
import com.sunsy.authserver.beans.po.AccessToken;
import com.sunsy.authserver.dao.AppClientDao;
import com.sunsy.authserver.enums.ErrorCode;
import com.sunsy.authserver.service.AccessTokenService;
import com.sunsy.authserver.service.AppClientService;
import com.sunsy.authserver.util.JwtUtil;
import com.sunsy.authserver.util.MD5;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * (AccessToken)表服务实现类
 *
 * @author makejava
 * @since 2021-07-20 14:34:08
 */
@Service("accessTokenService")
public class AccessTokenServiceImpl extends ServiceImpl<AccessTokenDao, AccessToken> implements AccessTokenService {

    private AppClientDao appClientDao;
    private RedisOperator redisOperator;
    private JwtUtil jwtUtil;

    @Autowired
    public void setJwtUtil(JwtUtil jwtUtil) {
        this.jwtUtil = jwtUtil;
    }

    @Autowired
    public void setRedisOperator(RedisOperator redisOperator) {
        this.redisOperator = redisOperator;
    }

    @Autowired
    public void setAppClientDao(AppClientDao appClientDao) {
        this.appClientDao = appClientDao;
    }


    @Override
    public AccessTokenVO createToken(AccessTokenDTO dto) {
        AppClient client = appClientDao.getApp(dto.getClientId());
        //检查缓存中是否存在对应client的token，如果存在，校验，如果不存在(可能存在穿透现象)，
        // 查询数据库是否存在，若数据库存在，将数据库的返回给他，然后更新缓存
        AccessToken accessToken = checkAccessToken(dto.getClientId(), client.getId());
        if (accessToken == null) {
            accessToken = new AccessToken();
            accessToken.setClientId(client.getId());
        }
        //如果历史记录过期了，，重新生成一个token
        if (!jwtUtil.verify(accessToken.getAccessToken())) {
            accessToken = createToken(dto, accessToken);
        }
        int i = accessToken.getId() == null ? baseMapper.insert(accessToken) : baseMapper.updateById(accessToken);
        redisOperator.hset(dto.getClientId(), "access_token", JSON.toJSONString(accessToken));
        return createVo(accessToken);
    }

    @Override
    public AccessTokenVO refreshToken(AccessTokenDTO dto) {
        AppClient client = appClientDao.getApp(dto.getClientId());
        AccessToken accessToken = checkAccessToken(dto.getClientId(), client.getId());
        if (accessToken == null) {
            ErrorCode.ACCESS_TOKEN_NOT_FOUNT.apiExc();
        }
        if (!dto.getRefreshToken().equals(accessToken.getRefreshToken())) {
            ErrorCode.REFRESH_TOKEN_EXC.apiExc();
        }
        accessToken = createToken(dto, accessToken);
        updateById(accessToken);
        redisOperator.hset(dto.getClientId(), "access_token", JSON.toJSONString(accessToken));
        return createVo(accessToken);
    }

    private AccessTokenVO createVo(AccessToken accessToken) {
        AccessTokenVO vo = new AccessTokenVO();
        vo.setExpiresIn(jwtUtil.getExpiration(accessToken.getAccessToken()));
        vo.setAccessToken(accessToken.getAccessToken());
        vo.setRefreshToken(accessToken.getRefreshToken());
        return vo;
    }

    /**
     * 负责根据数据生成token
     *
     * @param dto
     * @param accessToken
     * @return
     */
    private AccessToken createToken(AccessTokenDTO dto, AccessToken accessToken) {
        String token = jwtUtil.createToken(MD5.toMD5(JSON.toJSONString(dto)));
        dto.setAccessToken(token);
        String refreshToken = jwtUtil.createRefreshToken(MD5.toMD5(JSON.toJSONString(dto)));
        accessToken.setAccessToken(token);
        accessToken.setRefreshToken(refreshToken);
        return accessToken;
    }

    private AccessToken checkAccessToken(String clientId, Long cId) {
        String json = redisOperator.hget(clientId, "access_token", String.class);
        AccessToken accessToken = JSON.parseObject(json, AccessToken.class);
        if (accessToken == null) {
            accessToken = baseMapper.getTokenByClientId(cId);
        }
        return accessToken;
    }

}